package com.coszero.utils.decoration

import android.R
import android.content.Context
import android.content.res.Resources
import android.content.res.TypedArray
import android.graphics.Canvas
import android.graphics.Paint
import android.graphics.Rect
import android.graphics.drawable.Drawable
import android.view.View
import androidx.annotation.ColorRes
import androidx.annotation.DimenRes
import androidx.annotation.DrawableRes
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.GridLayoutManager.SpanSizeLookup
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.OrientationHelper
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.StaggeredGridLayoutManager

/**
 * @author xmqian
 * Created by yqritc on 2015/01/08.
 * git:https://github.com/hiphonezhu/RecyclerView-FlexibleDivider
 * @version 1
 */
abstract class FlexibleDividerDecoration protected constructor(builder: Builder<*>) :
    RecyclerView.ItemDecoration() {
    protected enum class DividerType {
        DRAWABLE, PAINT, COLOR, SPACE
    }

    protected var mDividerType: DividerType = DividerType.DRAWABLE
    protected var mVisibilityProvider: VisibilityProvider
    protected var mPaintProvider: PaintProvider? = null
    protected var mColorProvider: ColorProvider? = null
    protected var mDrawableProvider: DrawableProvider? = null
    protected var mSizeProvider: SizeProvider? = null
    protected var mSpaceProvider: SizeProvider? = null
    protected var mShowLastDivider: Boolean
    protected var mPositionInsideItem: Boolean
    private var mPaint: Paint? = null

    init {
        if (builder.mPaintProvider != null) {
            mDividerType = DividerType.PAINT
            mPaintProvider = builder.mPaintProvider
        } else if (builder.mColorProvider != null) {
            mDividerType = DividerType.COLOR
            mColorProvider = builder.mColorProvider
            mPaint = Paint()
            setSizeProvider(builder)
        } else if (builder.mSpaceProvider != null) {
            mDividerType = DividerType.SPACE
            mSpaceProvider = builder.mSpaceProvider
        } else {
            mDividerType = DividerType.DRAWABLE
            if (builder.mDrawableProvider == null) {
                val a: TypedArray = builder.mContext.obtainStyledAttributes(
                    ATTRS
                )
                val divider: Drawable? = a.getDrawable(0)
                a.recycle()
                mDrawableProvider = object : DrawableProvider {
                    override fun drawableProvider(position: Int, parent: RecyclerView?): Drawable? {
                        return divider
                    }
                }
            } else {
                mDrawableProvider = builder.mDrawableProvider
            }
            mSizeProvider = builder.mSizeProvider
        }

        mVisibilityProvider = builder.mVisibilityProvider
        mShowLastDivider = builder.mShowLastDivider
        mPositionInsideItem = builder.mPositionInsideItem
    }

    private fun setSizeProvider(builder: Builder<*>) {
        mSizeProvider = builder.mSizeProvider
        if (mSizeProvider == null) {
            mSizeProvider = object : SizeProvider {
                override fun dividerSize(position: Int, parent: RecyclerView?): Int {
                    return DEFAULT_SIZE
                }
            }
        }
    }

    override fun onDraw(c: Canvas, parent: RecyclerView, state: RecyclerView.State) {
        val adapter: RecyclerView.Adapter<*>? = parent.getAdapter()
        if (adapter == null) {
            return
        }

        val validChildCount: Int = parent.getChildCount()
        for (i in 0 until validChildCount) {
            val child: View = parent.getChildAt(i)
            val childPosition: Int = parent.getChildAdapterPosition(child)

            if (!hasDivider(parent, childPosition)) {
                continue
            }

            if (mVisibilityProvider.shouldHideDivider(childPosition, parent)) {
                continue
            }

            val bounds: Rect = getDividerBound(childPosition, parent, child)
            when (mDividerType) {
                DividerType.DRAWABLE -> {
                    val drawable: Drawable? =
                        mDrawableProvider!!.drawableProvider(childPosition, parent)
                    drawable!!.setBounds(bounds)
                    drawable.draw(c)
                }

                DividerType.PAINT -> {
                    mPaint = mPaintProvider!!.dividerPaint(childPosition, parent)
                    c.drawLine(
                        bounds.left.toFloat(),
                        bounds.top.toFloat(),
                        bounds.right.toFloat(),
                        bounds.bottom.toFloat(),
                        mPaint!!
                    )
                }

                DividerType.COLOR -> {
                    mPaint!!.setColor(mColorProvider!!.dividerColor(childPosition, parent))
                    mPaint!!.setStrokeWidth(
                        mSizeProvider!!.dividerSize(childPosition, parent).toFloat()
                    )
                    c.drawLine(
                        bounds.left.toFloat(),
                        bounds.top.toFloat(),
                        bounds.right.toFloat(),
                        bounds.bottom.toFloat(),
                        mPaint!!
                    )
                }

                DividerType.SPACE -> {}
            }
        }
    }

    /**
     * Whether child has divider
     *
     * @param parent
     * @param childPosition
     * @return true if child has divider
     */
    fun hasDivider(parent: RecyclerView, childPosition: Int): Boolean {
        if (mShowLastDivider) {
            return true
        } else if (this is VerticalDividerItemDecoration) {
            return hasVerticalDivider(parent, childPosition)
        } else if (this is HorizontalDividerItemDecoration) {
            return hasHorizontalDivider(parent, childPosition)
        }
        return false
    }

    private fun hasVerticalDivider(parent: RecyclerView, position: Int): Boolean {
        val adapter: RecyclerView.Adapter<*>? = parent.getAdapter()
        val itemCount: Int = adapter!!.getItemCount()
        val lastDividerOffset: Int = getLastDividerOffset(parent)


        if (parent.getLayoutManager() is GridLayoutManager) {
            val manager: GridLayoutManager? = parent.getLayoutManager() as GridLayoutManager?
            val spanCount: Int = manager!!.getSpanCount()
            //OrientationHelper.VERTICAL
            if (manager.getOrientation() == RecyclerView.VERTICAL) {
                return positionTotalSpanSize(manager, position) != spanCount
            } else {
                if (manager.getReverseLayout()) {
                    return manager.getSpanSizeLookup().getSpanGroupIndex(position, spanCount) != 0
                } else {
                    return position < itemCount - lastDividerOffset
                }
            }
        } else if (parent.getLayoutManager() is StaggeredGridLayoutManager) {
            val manager: StaggeredGridLayoutManager? =
                parent.getLayoutManager() as StaggeredGridLayoutManager?
            val params: StaggeredGridLayoutManager.LayoutParams = manager!!.findViewByPosition(
                position
            )!!
                .getLayoutParams() as StaggeredGridLayoutManager.LayoutParams
            val spanCount: Int = manager.getSpanCount()
            val spanIndex: Int = params.getSpanIndex()

            if (manager.getOrientation() == OrientationHelper.VERTICAL) {
                return spanIndex < spanCount - 1
            } else {
                var lastPosition: IntArray? = null
                if (manager.getReverseLayout()) {
                    lastPosition = manager.findFirstVisibleItemPositions(null)
                } else {
                    lastPosition = manager.findLastVisibleItemPositions(null)
                }
                var hasDirectionAlign: Boolean = false
                for (p: Int in lastPosition) {
                    if (p != position && p != -1) {
                        val params1: StaggeredGridLayoutManager.LayoutParams =
                            manager.findViewByPosition(
                                p
                            )!!
                                .getLayoutParams() as StaggeredGridLayoutManager.LayoutParams
                        if (params1.getSpanIndex() == spanIndex) {
                            hasDirectionAlign = true
                            break
                        }
                    }
                }
                return hasDirectionAlign
            }
        } else if (parent.getLayoutManager() is LinearLayoutManager) {
            if ((parent.getLayoutManager() as LinearLayoutManager).getReverseLayout()) {
                return position > 0
            } else {
                return position < itemCount - 1
            }
        }
        return false
    }

    private fun hasHorizontalDivider(parent: RecyclerView, position: Int): Boolean {
        val adapter: RecyclerView.Adapter<*>? = parent.getAdapter()
        val itemCount: Int = adapter!!.getItemCount()
        val lastDividerOffset: Int = getLastDividerOffset(parent)

        if (parent.getLayoutManager() is GridLayoutManager) {
            val manager: GridLayoutManager? = parent.getLayoutManager() as GridLayoutManager?
            val spanCount: Int = manager!!.getSpanCount()
            //OrientationHelper.VERTICAL
            if (manager.getOrientation() == RecyclerView.VERTICAL)  // 最后一行没有
            {
                if (manager.getReverseLayout()) {
                    val lookup: SpanSizeLookup = manager.getSpanSizeLookup()
                    return lookup.getSpanGroupIndex(position, spanCount) != 0
                } else {
                    return position < itemCount - lastDividerOffset
                }
            } else {
                return positionTotalSpanSize(manager, position) != spanCount
            }
        } else if (parent.getLayoutManager() is StaggeredGridLayoutManager) {
            val manager: StaggeredGridLayoutManager? =
                parent.getLayoutManager() as StaggeredGridLayoutManager?
            val params: StaggeredGridLayoutManager.LayoutParams = manager!!.findViewByPosition(
                position
            )!!
                .getLayoutParams() as StaggeredGridLayoutManager.LayoutParams
            val spanCount: Int = manager.getSpanCount()
            val spanIndex: Int = params.getSpanIndex()

            if (manager.getOrientation() == OrientationHelper.VERTICAL) {
                if (manager.getReverseLayout()) {
                    return position > spanCount - 1
                } else {
                    val lastPosition: IntArray = manager.findLastVisibleItemPositions(null)

                    var hasBottom: Boolean = false
                    for (p: Int in lastPosition) {
                        if (p != position && p != -1) {
                            val params1: StaggeredGridLayoutManager.LayoutParams =
                                manager.findViewByPosition(
                                    p
                                )!!
                                    .getLayoutParams() as StaggeredGridLayoutManager.LayoutParams
                            if (params1.getSpanIndex() == spanIndex) {
                                hasBottom = true
                                break
                            }
                        }
                    }
                    return hasBottom
                }
            } else {
                return spanIndex < spanCount - 1
            }
        } else if (parent.getLayoutManager() is LinearLayoutManager) {
            if ((parent.getLayoutManager() as LinearLayoutManager).getReverseLayout()) {
                return position > 0
            } else {
                return position < itemCount - 1
            }
        }
        return false
    }

    /**
     * @param manager
     * @param position
     * @return
     */
    protected fun positionTotalSpanSize(manager: GridLayoutManager, position: Int): Int {
        var totalSpanSize: Int = 0
        val spanSizeLookup: SpanSizeLookup = manager.getSpanSizeLookup()
        val spanCount: Int = manager.getSpanCount()
        val groupIndex: Int = spanSizeLookup.getSpanGroupIndex(position, spanCount)
        for (i in position downTo 0) {
            val thisGroupIndex: Int = spanSizeLookup.getSpanGroupIndex(i, spanCount)
            if (thisGroupIndex == groupIndex) {
                totalSpanSize += spanSizeLookup.getSpanSize(i)
            } else {
                break
            }
        }
        return totalSpanSize
    }

    override fun getItemOffsets(
        rect: Rect,
        v: View,
        parent: RecyclerView,
        state: RecyclerView.State
    ) {
        val position: Int = parent.getChildAdapterPosition(v)
        if (!hasDivider(parent, position)) {
            return
        }

        if (mVisibilityProvider.shouldHideDivider(position, parent)) {
            return
        }

        setItemOffsets(rect, position, parent)
    }

    /**
     * In the case mShowLastDivider = false,
     * Returns offset for how many views we don't have to draw a divider for,
     * for LinearLayoutManager it is as simple as not drawing the last child divider,
     * but for a GridLayoutManager it needs to take the span count for the last items into account
     * until we use the span count configured for the grid.
     *
     * @param parent RecyclerView
     * @return offset for how many views we don't have to draw a divider or 1 if its a
     * LinearLayoutManager
     */
    private fun getLastDividerOffset(parent: RecyclerView): Int {
        if (parent.getLayoutManager() is GridLayoutManager) {
            val layoutManager: GridLayoutManager? = parent.getLayoutManager() as GridLayoutManager?
            val spanSizeLookup: SpanSizeLookup = layoutManager!!.getSpanSizeLookup()
            val spanCount: Int = layoutManager.getSpanCount()
            val itemCount: Int = parent.getAdapter()!!.getItemCount()
            for (i in itemCount - 1 downTo 0) {
                if (spanSizeLookup.getSpanIndex(i, spanCount) == 0) {
                    return itemCount - i
                }
            }
        }

        return 1
    }

    protected abstract fun getDividerBound(position: Int, parent: RecyclerView, child: View): Rect

    protected abstract fun setItemOffsets(outRect: Rect, position: Int, parent: RecyclerView)

    /**
     * Interface for controlling divider visibility
     */
    interface VisibilityProvider {
        /**
         * Returns true if divider should be hidden.
         *
         * @param position Divider position (or group index for GridLayoutManager)
         * @param parent RecyclerView
         * @return True if the divider at position should be hidden
         */
        fun shouldHideDivider(position: Int, parent: RecyclerView?): Boolean
    }

    /**
     * Interface for controlling paint instance for divider drawing
     */
    interface PaintProvider {
        /**
         * Returns [Paint] for divider
         *
         * @param position Divider position (or group index for GridLayoutManager)
         * @param parent RecyclerView
         * @return Paint instance
         */
        fun dividerPaint(position: Int, parent: RecyclerView?): Paint
    }

    /**
     * Interface for controlling divider color
     */
    interface ColorProvider {
        /**
         * Returns [android.graphics.Color] value of divider
         *
         * @param position Divider position (or group index for GridLayoutManager)
         * @param parent RecyclerView
         * @return Color value
         */
        fun dividerColor(position: Int, parent: RecyclerView?): Int
    }

    /**
     * Interface for controlling drawable object for divider drawing
     */
    interface DrawableProvider {
        /**
         * Returns drawable instance for divider
         *
         * @param position Divider position (or group index for GridLayoutManager)
         * @param parent RecyclerView
         * @return Drawable instance
         */
        fun drawableProvider(position: Int, parent: RecyclerView?): Drawable?
    }

    /**
     * Interface for controlling divider size
     */
    interface SizeProvider {
        /**
         * Returns size value of divider.
         * Height for horizontal divider, width for vertical divider
         *
         * @param position Divider position (or group index for GridLayoutManager)
         * @param parent RecyclerView
         * @return Size of divider
         */
        fun dividerSize(position: Int, parent: RecyclerView?): Int
    }

    open class Builder<T : Builder<*>?>(
        val mContext: Context
    ) {
        protected var mResources: Resources
        var mPaintProvider: PaintProvider? = null
        var mColorProvider: ColorProvider? = null
        var mDrawableProvider: DrawableProvider? = null
        var mSizeProvider: SizeProvider? = null
        var mSpaceProvider: SizeProvider? = null
        var mVisibilityProvider: VisibilityProvider = object : VisibilityProvider {
            override fun shouldHideDivider(position: Int, parent: RecyclerView?): Boolean {
                return false
            }
        }
        var mShowLastDivider: Boolean = false
        var mPositionInsideItem: Boolean = false

        init {
            mResources = mContext.getResources()
        }

        fun paint(paint: Paint): T {
            return paintProvider(object : PaintProvider {
                override fun dividerPaint(position: Int, parent: RecyclerView?): Paint {
                    return paint
                }
            })
        }

        fun paintProvider(provider: PaintProvider?): T {
            mPaintProvider = provider
            return this as T
        }

        fun color(color: Int): T {
            return colorProvider(object : ColorProvider {
                override fun dividerColor(position: Int, parent: RecyclerView?): Int {
                    return color
                }
            })
        }

        fun colorResId(@ColorRes colorId: Int): T {
            return color(ContextCompat.getColor(mContext, colorId))
        }

        fun colorProvider(provider: ColorProvider?): T {
            mColorProvider = provider
            return this as T
        }

        fun drawable(@DrawableRes id: Int): T {
            return drawable(ContextCompat.getDrawable(mContext, id))
        }

        fun drawable(drawable: Drawable?): T {
            return drawableProvider(object : DrawableProvider {
                override fun drawableProvider(position: Int, parent: RecyclerView?): Drawable? {
                    return drawable
                }
            })
        }

        fun drawableProvider(provider: DrawableProvider?): T {
            mDrawableProvider = provider
            return this as T
        }

        fun size(size: Int): T {
            return sizeProvider(object : SizeProvider {
                override fun dividerSize(position: Int, parent: RecyclerView?): Int {
                    return size
                }
            })
        }

        fun sizeResId(@DimenRes sizeId: Int): T {
            return size(mResources.getDimensionPixelSize(sizeId))
        }

        fun sizeProvider(provider: SizeProvider?): T {
            mSizeProvider = provider
            return this as T
        }

        fun space(space: Int): T {
            return spaceProvider(object : SizeProvider {
                override fun dividerSize(position: Int, parent: RecyclerView?): Int {
                    return space
                }
            })
        }

        fun spaceResId(@DimenRes spaceId: Int): T {
            return space(mResources.getDimensionPixelSize(spaceId))
        }

        fun spaceProvider(provider: SizeProvider?): T {
            mSpaceProvider = provider
            return this as T
        }

        fun visibilityProvider(provider: VisibilityProvider): T {
            mVisibilityProvider = provider
            return this as T
        }

        fun showLastDivider(): T {
            mShowLastDivider = true
            return this as T
        }

        fun positionInsideItem(positionInsideItem: Boolean): T {
            mPositionInsideItem = positionInsideItem
            return this as T
        }

        protected fun checkBuilderParams() {
            if (mPaintProvider != null) {
                require(mColorProvider == null) { "Use setColor method of Paint class to specify line color. Do not provider ColorProvider if you set PaintProvider." }
                require(mSizeProvider == null) { "Use setStrokeWidth method of Paint class to specify line size. Do not provider SizeProvider if you set PaintProvider." }
            }
        }
    }

    companion object {
        private const val DEFAULT_SIZE: Int = 2
        private val ATTRS: IntArray = intArrayOf(
            R.attr.listDivider
        )
    }
}